package ace.module.product.core.impl.manager.impl;

import ace.cmp.core.constants.CoreConstant;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.model.SaveContext;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.model.SaveListContext;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.utils.SaveContextUtils;
import ace.cmp.util.AceAssertUtils;
import ace.cmp.util.AceEnumUtils;
import ace.cmp.util.AceStreamUtils;
import ace.module.common.core.impl.manager.impl.AbstractEntityOfAppManager;
import ace.module.product.api.enums.PmsProductDetailTypeEnum;
import ace.module.product.api.model.dto.PmsProductDetailDto;
import ace.module.product.api.model.input.PmsProductDetailSaveInput;
import ace.module.product.core.impl.dao.entity.PmsProduct;
import ace.module.product.core.impl.dao.entity.PmsProductDetail;
import ace.module.product.core.impl.dao.entity.PmsProductSku;
import ace.module.product.core.impl.dao.repository.PmsProductDetailRepository;
import ace.module.product.core.impl.manager.PmsProductDetailManager;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

/**
 * 商品详情 spring data jdbc querydsl rsql manager implement.
 *
 * @author Caspar
 */
@Slf4j
@AllArgsConstructor
@Component
public class PmsProductDetailManagerImpl
    extends AbstractEntityOfAppManager<PmsProductDetail, Long, PmsProductDetailDto>
    implements PmsProductDetailManager {

  private final PmsProductDetailRepository pmsProductDetailRepository;

  private static final List<PmsProductDetailTypeEnum> PRODUCT_DETAIL_TYPE_ENUMS = Collections.unmodifiableList(
      Arrays.asList(
          PmsProductDetailTypeEnum.PRODUCT_DETAIL_DEFAULT,
          PmsProductDetailTypeEnum.PRODUCT_DETAIL_PC,
          PmsProductDetailTypeEnum.PRODUCT_DETAIL_MOBILE
      )
  );

  private static final List<PmsProductDetailTypeEnum> PRODUCT_SKU_DETAIL_TYPE_ENUMS = Collections.unmodifiableList(
      Arrays.asList(
          PmsProductDetailTypeEnum.PRODUCT_SKU_DETAIL_DEFAULT,
          PmsProductDetailTypeEnum.PRODUCT_SKU_DETAIL_PC,
          PmsProductDetailTypeEnum.PRODUCT_SKU_DETAIL_MOBILE
      )
  );

  @Override
  public SaveListContext<PmsProductDetail> assembleProductSaveListContext(List<PmsProductDetailSaveInput> pmsProductDetailSaveInputs, SaveContext<PmsProduct> pmsProductSaveContext) {
    List<SaveContext<PmsProductDetail>> productDetails = pmsProductDetailSaveInputs
        .stream()
        .map(detailInput -> this.assembleProductSaveContext(detailInput, pmsProductSaveContext))
        .collect(Collectors.toList());

    productDetails = this.filterDuplicate(productDetails);

    return SaveContextUtils.of(productDetails);
  }

  @Override
  public SaveContext<PmsProductDetail> assembleProductSaveContext(PmsProductDetailSaveInput detailInput, SaveContext<PmsProduct> pmsProductSaveContext) {
    PmsProductDetailTypeEnum typeEnum = AceEnumUtils.getEnum(PmsProductDetailTypeEnum.class, detailInput.getType());
    this.checkTypeIsProductDetail(typeEnum);
    PmsProduct pmsProduct = pmsProductSaveContext.getData();

    PmsProductDetail productDetail = null;
    if (pmsProductSaveContext.isModify()) {
      Long productSkuId = CoreConstant.ZERO_LONG;
      productDetail = pmsProductDetailRepository.findByPmsAppIdAndProductIdAndProductSkuIdAndTypeAndDeleteFlag(
          pmsProduct.getPmsAppId(),
          pmsProduct.getId(),
          productSkuId,
          typeEnum.getCode(),
          CoreConstant.DELETE_FLAG_NOT
      );
    }
    boolean isAdd = Objects.isNull(productDetail);
    if (isAdd) {
      productDetail = PmsProductDetail.builder()
          .id(this.getNewId())
          .pmsAppId(pmsProduct.getPmsAppId())
          .productId(pmsProduct.getId())
          .productSkuId(CoreConstant.ZERO_LONG)
          .sort(CoreConstant.DEFAULT_SORT)

          .build();
    }
    BeanUtils.copyProperties(detailInput, productDetail);
    return SaveContextUtils.of(productDetail, isAdd);
  }

  @Override
  public SaveListContext<PmsProductDetail> assembleProductSkuSaveListContext(List<PmsProductDetailSaveInput> pmsProductDetailSaveInputs, SaveContext<PmsProductSku> pmsProductSkuSaveContext) {
    List<SaveContext<PmsProductDetail>> productDetails = pmsProductDetailSaveInputs
        .stream()
        .map(detailInput -> this.assembleProductSkuSaveContext(detailInput, pmsProductSkuSaveContext))
        .collect(Collectors.toList());

    productDetails = this.filterDuplicate(productDetails);

    return SaveContextUtils.of(productDetails);
  }

  @Override
  public SaveContext<PmsProductDetail> assembleProductSkuSaveContext(PmsProductDetailSaveInput detailInput,
      SaveContext<PmsProductSku> pmsProductSkuSaveContext) {
    PmsProductDetailTypeEnum typeEnum = AceEnumUtils.getEnum(PmsProductDetailTypeEnum.class, detailInput.getType());
    this.checkTypeIsProductSkuDetail(typeEnum);
    PmsProductSku pmsProductSku = pmsProductSkuSaveContext.getData();
    PmsProductDetail productDetail = null;
    if (pmsProductSkuSaveContext.isModify()) {
      productDetail = pmsProductDetailRepository.findByPmsAppIdAndProductIdAndProductSkuIdAndTypeAndDeleteFlag(
          pmsProductSku.getPmsAppId(),
          pmsProductSku.getProductId(),
          pmsProductSku.getId(),
          typeEnum.getCode(),
          CoreConstant.DELETE_FLAG_NOT
      );
    }
    boolean isAdd = Objects.isNull(productDetail);
    if (isAdd) {
      productDetail = PmsProductDetail.builder()
          .id(this.getNewId())
          .pmsAppId(pmsProductSku.getPmsAppId())
          .productId(pmsProductSku.getId())
          .productSkuId(pmsProductSku.getId())
          .sort(CoreConstant.DEFAULT_SORT)

          .build();
    }
    BeanUtils.copyProperties(detailInput, productDetail);
    return SaveContextUtils.of(productDetail, isAdd);
  }

  private List<SaveContext<PmsProductDetail>> filterDuplicate(List<SaveContext<PmsProductDetail>> pmsProductDetailSaveContexts) {
    return AceStreamUtils.ofNullable(pmsProductDetailSaveContexts)
        // 过滤重复数据,只保留其中一个
        .collect(
            () -> new HashMap<String, SaveContext<PmsProductDetail>>(),
            (m, entry) -> {
              String id = String.format("%s%s%s",
                  entry.getData().getProductId(),
                  entry.getData().getProductSkuId(),
                  entry.getData().getType());
              m.put(id, entry);
            },
            HashMap::putAll
        ).values()
        .stream()
        .collect(Collectors.toList());
  }


  private void checkTypeIsProductDetail(PmsProductDetailTypeEnum typeEnum) {
    PRODUCT_DETAIL_TYPE_ENUMS.forEach(checkType -> {
      AceAssertUtils.isFalse(checkType.equals(typeEnum), "[%s]请输入正确的商品详情类型", typeEnum.getCode());
    });
  }

  private void checkTypeIsProductSkuDetail(PmsProductDetailTypeEnum typeEnum) {
    PRODUCT_SKU_DETAIL_TYPE_ENUMS.forEach(checkType -> {
      AceAssertUtils.isFalse(checkType.equals(typeEnum), "[%s]请输入正确的商品sku详情类型", typeEnum.getCode());
    });
  }
}
